/*
 * CompressionProcessProvider
 *
 *  Created on: 10/06/2011
 *      Author: Agustina I. Stekolschik & Ezequiel Palumbo
 */

#include <CompressionProcess.h>
#include <string.h>
#include <Logging.h>
#include <CodingLogicProvider.h>
#include <StructModel.h>
#include <DataInputOutput.h>
#include <memory>
#include <BlockSorting.h>
#include <MoveToFront.h>

const string COMPRESSED_FILE_EXTENSION(".vavoom");

using logging::Logger;
using logging::LoggerFactory;
using logging::Level;

using namespace model;
using namespace DataInputOutput;
using namespace processes;

namespace providers{

CompressionProcess::CompressionProcess(const char* aFileRoute){
	fileRoute = aFileRoute;
}

void CompressionProcess::doCompressionProcess(){
	Logger& logger = LoggerFactory::getInstance().getLogger("Process.Compression");
	logger.log(logging::DEBUG, "Bootstrapping compression command execution.");

	logger.log(logging::TRACE, "Creating components for pipeline processes.");

	logger.log(logging::TRACE, "Instantiating DataInput for reading data from file.");
	FileDataInput fileDataInput(this->fileRoute);

	logger.log(logging::TRACE, "Instantiating Block Sorting Transformation Process.");
	BlockSortingForwardTransformationProcess blockSortingProcess;

	logger.log(logging::TRACE, "Instantiating Buffered Decorator for BlockSorting process.");
	BufferedDecoratorDataInput blockSortingDataInput(&blockSortingProcess, &fileDataInput);

	logger.log(logging::TRACE, "Instantiating DataUnitFactory, default to CHAR message type.");
	CharDataUnitFactory dataUnitsFactory;

	logger.log(logging::TRACE, "Instantiating MoveToFront Transformation Process.");
	MoveToFrontForwardTransformationProcess mtfProcess(dataUnitsFactory.getAll());

	logger.log(logging::TRACE, "Instantiating Decorator for MTF process.");
	DecoratorDataInput moveToFrontDataInput(&mtfProcess, &blockSortingDataInput);

	logger.log(logging::TRACE, "Instantiating BitDataOutput for codification.");
	string fileNameStr;
	const char* compressedFileName = (fileNameStr + this->fileRoute + COMPRESSED_FILE_EXTENSION).c_str();
	FileBitDataOutput fileBitDataOutput(compressedFileName);

	logger.log(logging::TRACE, "Instantiating Logic Provider. Using ARITHMETIC.");
	ArithmeticCodingLogicProvider provider;

	logger.log(logging::TRACE, "Instantiating Fenwick's Structured Model.");
	StructModel model(provider);
	logger.log(logging::TRACE, "Initialization of levels now.");
	model.initContexts();

	logger.log(logging::DEBUG, "Invoking compression method.");
	model.compress(&moveToFrontDataInput, &fileBitDataOutput);

	logger.log(logging::DEBUG, "Exiting compression process command.");
}

CompressionProcess::~CompressionProcess() {

}

DecompressionProcess::DecompressionProcess(const char* aFileRoute){
	this->fileRoute = aFileRoute;
}

void DecompressionProcess::doCompressionProcess(){
	Logger& logger = LoggerFactory::getInstance().getLogger("Process.Decompression");
	logger.log(logging::DEBUG, "Bootstrapping decompression command execution.");

	logger.log(logging::TRACE, "Instantiating File Bit Data Input for decoding.");
	FileBitDataInput fileBitDataInput(this->fileRoute);

	logger.log(logging::TRACE, "Instantiating File Data Output.");
	string fileNameStr(this->fileRoute);
	fileNameStr.replace(
			fileNameStr.length() - COMPRESSED_FILE_EXTENSION.length(),
			COMPRESSED_FILE_EXTENSION.length(),
			"").c_str();
	FileDataOutput fileDataOutput(fileNameStr.c_str());

	logger.log(logging::TRACE, "Instantiating Block Sorting Transformation Process.");
	BlockSortingBackwardTransformationProcess bsProcess;

	logger.log(logging::TRACE, "Instantiating Buffered Decorator Data Output for BS process.");
	BufferedDecoratorDataOutput bsDataOutput(&bsProcess, &fileDataOutput);

	logger.log(logging::TRACE, "Instantiating Data Unit Factory, default to CHAR message type.");
	CharDataUnitFactory dataUnitFactory;

	logger.log(logging::TRACE, "Instantiating MoveToFront Transformation Process");
	MoveToFrontBackwardTransformationProcess mtfProcess(dataUnitFactory.getAll());

	logger.log(logging::TRACE, "Instantiating Decorator Data Output for MTF process.");
	DecoratorDataOutput mtfDataOutput(&mtfProcess, &bsDataOutput);

	logger.log(logging::TRACE, "Instantiating Logic Provider. Using ARITHMETIC.");
	ArithmeticCodingLogicProvider provider;

	logger.log(logging::TRACE, "Instantiating Fenwick's Structured Model.");
	StructModel model(provider);
	logger.log(logging::TRACE, "Initialization of levels now.");
	model.initContexts();

	logger.log(logging::DEBUG, "Invoking decompression method.");
	model.decompress(&fileBitDataInput, &mtfDataOutput, &dataUnitFactory);

	logger.log(logging::DEBUG, "Exiting decompression command.");
}

DecompressionProcess::~DecompressionProcess(){

}

bool CompressionProcessProvider::compression = true;
const char* CompressionProcessProvider::fileRoute;

CompressionCommand* CompressionProcessProvider::createCompressionCommand(){
	if (compression){
		return new CompressionProcess(fileRoute);
	}else{
		return new DecompressionProcess(fileRoute);
	}
}

void CompressionProcessProvider::setCompressionCommand(const char* methodName){
	compression = !strcmp(methodName,"compression");
}

void CompressionProcessProvider::setCompressionFileRoute(const char* aRoute){
	fileRoute = aRoute;
}

}
